// Copyright 2017 Baidu Inc. All Rights Reserved.
// Author: Duan Lian (duanlian01@baidu.com)
//
// Description: OTA Shared Buffer

#include "baidu_iot_ota_shared_buffer.h"
#include <assert.h>
#include "duer_log.h"
#include "heap_monitor.h"
#include "baidu_iot_ota_log.h"

namespace duer {

SharedBuffer::SharedBuffer(unsigned int size) :
         _m_n_buffer_size(size),
         _m_n_write_offset(0),
         _m_n_data_size(0),
         _sem_read(0),
         _sem_write(1) {
    _m_p_buffer = NEW(OTA) unsigned char[size];
}

SharedBuffer::~SharedBuffer() {
    if (_m_p_buffer) {
        delete[] _m_p_buffer;
        _m_p_buffer = NULL;
    }
}

unsigned int SharedBuffer::read(unsigned char out[], unsigned int size) {
    OTA_FUNC_ENTER();

    _sem_read.wait();
    DUER_LOGD("_m_n_data_size:%d, size:%d", _m_n_data_size, size);

    unsigned int n_read_bytes = (size >= _m_n_data_size) ? _m_n_data_size : size;
    if (n_read_bytes > 0) {
        memcpy(out, _m_p_buffer, n_read_bytes);
        _m_n_data_size -= n_read_bytes;

        if (_m_n_data_size > 0) {
            memmove(_m_p_buffer, _m_p_buffer + n_read_bytes, _m_n_data_size);
            _sem_read.release();
        } else {
            assert(0 == _m_n_data_size);
            _sem_write.release();
        }

        DUER_LOGD("Read bytes:%d", n_read_bytes);
    } else {
        DUER_LOGD("Read complete:%d", n_read_bytes);
    }

    OTA_FUNC_LEAVE();

    return n_read_bytes;
}

unsigned int SharedBuffer::write(const unsigned char in[], unsigned int size) {
    OTA_FUNC_ENTER();

    _sem_write.wait();
    assert(0 == _m_n_data_size && size > 0);

    unsigned int n_write_bytes = (size >= _m_n_buffer_size) ? _m_n_buffer_size : size;
    if (n_write_bytes > 0) {
        memmove(_m_p_buffer, in, n_write_bytes);
        _m_n_data_size += n_write_bytes;
        _sem_read.release();
    } else {
        _sem_write.release();
        n_write_bytes = 0;
    }

    DUER_LOGD("Write %d bytes", n_write_bytes);

    OTA_FUNC_LEAVE();

    return n_write_bytes;
}

void SharedBuffer::wait() {
    _sem_write.wait();
    _sem_write.release();
    _sem_read.release();
}

} // namespace duer
